#include "PyInteraction.h"

int sciIN, sciINEND;
BOOL consoleLoaded = FALSE;
PyObject *pynpp_module;
PyObject *npp_module;
PyObject *console_module;
PyObject *const_module;
PyObject *langs_module;
PyObject *editor_module;

// pynpp.console Module Functions
static PyObject* pynpp_Stdout(PyObject* self, PyObject* pArgs)
{
	char* LogStr = NULL;
	if ( !PyArg_ParseTuple(pArgs, "s", &LogStr) )
	{
		ERR_WIN(L"pynpp_Stdout:PyArg_ParseTuple error.");
		return NULL;
	}

	LogPy( (int) strlen(LogStr), LogStr);

	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject* pynpp_Stderr(PyObject* self, PyObject* pArgs)
{
	char* LogStr = NULL;
	if ( !PyArg_ParseTuple(pArgs, "s", &LogStr) )
	{
		ERR_WIN(L"pynpp_Stderr error.");
		return NULL;
	}

	LogPy( (int) strlen(LogStr), LogStr);

	Py_INCREF(Py_None);
	return Py_None;
}


// pynpp.npp Module Functions
static PyObject* pynpp_getNppPath(PyObject* self, PyObject* pArgs)
{
	char path[MAX_PATH];

	SendNpp(NPPM_GETNPPDIRECTORY, 0, path);

	return Py_BuildValue("s", path);
}

static PyObject* pynpp_getCWD(PyObject* self, PyObject* pArgs)
{
	char path[MAX_PATH];

	SendNpp(NPPM_GETCURRENTDIRECTORY, 0, path);

	return Py_BuildValue("s", path);
}

static PyObject* pynpp_getPluginsConfDir(PyObject* self, PyObject* pArgs)
{
	char path[MAX_PATH];

	SendNpp(NPPM_GETPLUGINSCONFIGDIR, MAX_PATH, path);

	return Py_BuildValue("s", path);
}

static PyObject* pynpp_getLanguage(PyObject* self, PyObject* pArgs)
{
	int lang;

	SendNpp(NPPM_GETCURRENTLANGTYPE, 0, &lang);

	return Py_BuildValue("i", lang);
}

static PyObject* pynpp_setLanguage(PyObject* self, PyObject* pArgs)
{
	int lang = L_TXT;

	if ( !PyArg_ParseTuple(pArgs, "i", &lang) )
		return NULL;

	SendNpp(NPPM_SETCURRENTLANGTYPE, 0, lang);

	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject* pynpp_getFileName(PyObject* self, PyObject* pArgs)
{
	char name[MAX_PATH];

	SendNpp(NPPM_GETFILENAME, 0, name);

	return Py_BuildValue("s", name);
}

static PyObject* pynpp_getFileExtension(PyObject* self, PyObject* pArgs)
{
	char ext[MAX_PATH];

	SendNpp(NPPM_GETEXTPART, 0, ext);

	return Py_BuildValue("s", ext);
}

static PyObject* pynpp_getFilePath(PyObject* self, PyObject* pArgs)
{
	char path[MAX_PATH];

	SendNpp(NPPM_GETFULLCURRENTPATH, 0, path);

	return Py_BuildValue("s", path);
}

static PyObject* pynpp_getNOpenFiles(PyObject* self, PyObject* pArgs)
{
	int sci = ALL_OPEN_FILES, num = 0;

	if ( !PyArg_ParseTuple(pArgs, "|i", &sci) )
		return NULL;

	num = (int) SendNpp(NPPM_GETNBOPENFILES, 0, sci);

	return Py_BuildValue("i", num);
}

static PyObject* pynpp_getOpenFiles(PyObject* self, PyObject* pArgs)
{
	int num = 0, i;
	char **names;
	PyObject *result;

	num = (int) SendNpp(NPPM_GETNBOPENFILES, 0, ALL_OPEN_FILES);
	names = (char**) malloc(num * sizeof(char*));
	for (i = 0; i < num; i++)
		names[i] = (char*) malloc(MAX_PATH);

	SendNpp(NPPM_GETOPENFILENAMES, names, num);

	result = PyList_New(num);
	for (i = 0; i < num; i++)
		PyList_SetItem(result, i, Py_BuildValue("s", names[i]));

	return result;
}

/*static PyObject* pynpp_openFile(PyObject* self, PyObject* pArgs)
{
	char file[MAX_PATH];

	if ( !PyArg_ParseTuple(pArgs, "s", &file) )
		return NULL;

	if ( SendNpp(NPPM_DOOPEN, 0, file) == 0 )
	{
		Py_INCREF(Py_False);
		return Py_False;
	}
	else
	{
		Py_INCREF(Py_True);
		return Py_True;
	}
}*/

static PyObject* pynpp_reloadFile(PyObject* self, PyObject* pArgs)
{
	int alert = FALSE;
	char *path = "";

	if ( !PyArg_ParseTuple(pArgs, "|si", &path, &alert) )
		return NULL;

	if (strcmp(path, "") == 0)
	{
		path = (char*) malloc(MAX_PATH * sizeof(char));
		SendNpp(NPPM_GETFULLCURRENTPATH, 0, path);
	}

	SendNpp(NPPM_RELOADFILE, alert, path);

	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject* pynpp_switchToFile(PyObject* self, PyObject* pArgs)
{
	char *path = NULL;

	if ( !PyArg_ParseTuple(pArgs, "s", &path) )
		return NULL;

	SendNpp(NPPM_SWITCHTOFILE, 0, path);

	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject* pynpp_saveFile(PyObject* self, PyObject* pArgs)
{
	SendNpp(NPPM_SAVECURRENTFILE, 0, 0);

	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject* pynpp_saveAllFiles(PyObject* self, PyObject* pArgs)
{
	SendNpp(NPPM_SAVEALLFILES, 0, 0);

	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject* pynpp_findInFilesDlg(PyObject* self, PyObject* pArgs)
{
	char *dir = NULL, *filters = NULL;

	if ( !PyArg_ParseTuple(pArgs, "|ss", &dir, &filters) )
		return NULL;

	SendNpp(NPPM_LAUNCHFINDINFILESDLG, dir, filters);

	Py_INCREF(Py_None);
	return Py_None;
}


// pynpp.editor Module Functions
static PyObject* pynpp_getCurrentWord(PyObject* self, PyObject* pArgs)
{
	char word[MAX_PATH];

	SendNpp(NPPM_GETCURRENTWORD, 0, word);

	return Py_BuildValue("s", word);
}

static PyMethodDef npp_methods[] = {
	{"getNppPath", pynpp_getNppPath, METH_VARARGS, "Returns the path of the Notepad++ executable."},
	{"getCWD", pynpp_getCWD, METH_VARARGS, "Returns the current working directory."},
	{"getPluginsConfDir", pynpp_getPluginsConfDir, METH_VARARGS, "Returns the directory path for the plugins config directory."},
	{"getLanguage", pynpp_getLanguage, METH_VARARGS, "Returns the language of the current file."},
	{"setLanguage", pynpp_setLanguage, METH_VARARGS, "Sets the language of the current file."},
	{"getFileName", pynpp_getFileName, METH_VARARGS, "Returns the name of the current file."},
	{"getFileExtension", pynpp_getFileExtension, METH_VARARGS, "Returns the extension of the current file."},
	{"getFilePath", pynpp_getFilePath, METH_VARARGS, "Returns the full path of the current file."},
	{"getNOpenFiles", pynpp_getNOpenFiles, METH_VARARGS, "Returns the the number of open files."},
	{"getOpenFiles", pynpp_getOpenFiles, METH_VARARGS, "Returns the paths of all open files in a list."},
	//{"openFile", pynpp_openFile, METH_VARARGS, "Opens a file."},
	{"reloadFile", pynpp_reloadFile, METH_VARARGS, "Reloads the current file."},
	{"switchToFile", pynpp_switchToFile, METH_VARARGS, "Switches to the file with the name filename."},
	{"saveFile", pynpp_saveFile, METH_VARARGS, "Saves the current file."},
	{"saveAllFiles", pynpp_saveAllFiles, METH_VARARGS, "Saves all the open files."},
	{"findInFilesDlg", pynpp_findInFilesDlg, METH_VARARGS, "Opens the Find in Files dialog."},
	{NULL, NULL, 0, NULL}
};
static struct PyModuleDef npp_method = {
	PyModuleDef_HEAD_INIT, "npp", "Notepad++ module.", -1, npp_methods,
	NULL, NULL, NULL, NULL
};
static PyObject* PyInit_npp(void)
{
	return PyModule_Create(&npp_method);
}

static PyMethodDef editor_methods[] = {
	{"getCurrentWord", pynpp_getCurrentWord, METH_VARARGS, "Returns the word on which the cursor is over."},
	{NULL, NULL, 0, NULL}
};
static struct PyModuleDef editor_method = {
	PyModuleDef_HEAD_INIT, "editor", "Text editor module.", -1, editor_methods,
	NULL, NULL, NULL, NULL
};
static PyObject* PyInit_editor(void)
{
	return PyModule_Create(&editor_method);
}


static PyMethodDef console_methods[] = {
	{"stdout", pynpp_Stdout, METH_VARARGS, "Logs stdout."},
	{"stderr", pynpp_Stderr, METH_VARARGS, "Logs stderr."},
	{NULL, NULL, 0, NULL}
};
static struct PyModuleDef console_method = {
	PyModuleDef_HEAD_INIT, "console", "Console module.", -1, console_methods,
	NULL, NULL, NULL, NULL
};
static PyObject* PyInit_console(void)
{
	return PyModule_Create(&console_method);
}

static PyMethodDef no_methods[] = {
	{NULL, NULL, 0, NULL}
};
static struct PyModuleDef langs_method = {
	PyModuleDef_HEAD_INIT, "langs", "Languages module.", -1, no_methods,
	NULL, NULL, NULL, NULL
};
static PyObject* PyInit_langs(void)
{
	return PyModule_Create(&langs_method);
}

static struct PyModuleDef pynpp_method = {
	PyModuleDef_HEAD_INIT,
	"pynpp", 
	"This is the pynpp module!!! Python plugins for Notepad++. Hurray.\n\n"
	"Author: Joao Moreno <alph.pt [on] gmail.com>\n"
	"Site: http://code.google.com/p/pynpp/ \n"
	"Full Docs:http://code.google.com/p/pynpp/wiki/pynppModuleReference \n",
	-1,
	no_methods,
	NULL, NULL, NULL, NULL
};

static struct PyModuleDef const_method = {
	PyModuleDef_HEAD_INIT, "const", "Constants module.", -1, no_methods,
	NULL, NULL, NULL, NULL
};
static PyObject* PyInit_const(void)
{
	return PyModule_Create(&const_method);
}


void SetupNpp(PyObject* m)
{
	/* if ((npp_module = Py_InitModule3("npp", npp_methods,
		"Notepad++ module.")) == NULL ||
	*/
	if ((npp_module = PyInit_npp()) == NULL ||
		PyModule_AddObject(m, "npp", npp_module) == -1)
	{
		ERR_WIN(L"Error initializing npp module.");
		_exit(-1);
	}
}

void SetupEditor(PyObject* m)
{
	/*if ((editor_module = Py_InitModule("editor", editor_methods,
		"Text editor module.")) == NULL ||*/
	if ((editor_module = PyInit_editor()) == NULL ||
		PyModule_AddObject(m, "editor", editor_module) == -1)
	{
		ERR_WIN(L"Error initializing editor module.");
		_exit(-1);
	}
}

void SetupConsole(PyObject* m)
{
	/*if ((console_module = Py_InitModule3("console", console_methods,
		"Console module.")) == NULL ||*/
	if ((console_module = PyInit_console()) == NULL ||
		PyModule_AddObject(m, "console", console_module) == -1)
	{
		ERR_WIN(L"Error initializing console module.");
		_exit(-1);
	}

	//int ret = PyRun_SimpleString(
	//"import sys\n"
	//"from pynpp import console\n"
	//"from time import time, ctime\n"
	//"class pynppStdoutLogger:\n"
	//"\tdef write(self, str):\n"
	//"\t\tconsole.stdout(str)\n"
	//"class pynppStderrLogger:\n"
	//"\tdef write(self, str):\n"
	//"\t\tconsole.stderr(str)\n"
	//"sys.stdout = pynppStdoutLogger()\n"
	//"sys.stderr = pynppStderrLogger()\n"
	//"print( ' * The console was loaded successfully.')\n");

	//consoleLoaded = TRUE;
}

void ImportStdFile()
{
	FILE *fp;
	errno_t err;

	err = fopen_s(&fp, "startup.py", "r+");
	if ( err != 0 )
	{
		ERR_WIN(L"No startup.py!");
	}
	if (PyRun_SimpleFile(fp, "startup.py") < 0)
	{
		INFO_WIN(L"ImportStdFile Error.");
	}
	fclose(fp);

	consoleLoaded = TRUE;
}

void SetupConst(PyObject* m)
{
	//if ((const_module = Py_InitModule3("const", no_methods,
	//	"Constants module.")) == NULL ||
	if ((const_module = PyInit_const()) == NULL ||
		PyModule_AddObject(m, "const", const_module) == -1)
	{
		ERR_WIN(L"Error initializing const module.");
		_exit(-1);
	}

	PyModule_AddIntConstant(const_module, "PRIMARY_VIEW", PRIMARY_VIEW);
	PyModule_AddIntConstant(const_module, "SECOND_VIEW", SECOND_VIEW);
	
	//if ((langs_module = Py_InitModule3("langs", no_methods,
	//	"Languages module.")) == NULL ||
	if ((langs_module = PyInit_langs()) == NULL ||
		PyModule_AddObject(const_module, "langs", langs_module) == -1)
	{
		ERR_WIN(L"Error initializing langs module.");
		_exit(-1);
	}
	
	PyModule_AddIntConstant(langs_module, "L_TXT", L_TXT);
	PyModule_AddIntConstant(langs_module, "L_PHP", L_PHP);
	PyModule_AddIntConstant(langs_module, "L_C", L_C);
	PyModule_AddIntConstant(langs_module, "L_CPP", L_CPP);
	PyModule_AddIntConstant(langs_module, "L_CS", L_CS);
	PyModule_AddIntConstant(langs_module, "L_OBJC", L_OBJC);
	PyModule_AddIntConstant(langs_module, "L_JAVA", L_JAVA);
	PyModule_AddIntConstant(langs_module, "L_RC", L_RC);
	PyModule_AddIntConstant(langs_module, "L_HTML", L_HTML);
	PyModule_AddIntConstant(langs_module, "L_XML", L_XML);
	PyModule_AddIntConstant(langs_module, "L_MAKEFILE", L_MAKEFILE);
	PyModule_AddIntConstant(langs_module, "L_PASCAL", L_PASCAL);
	PyModule_AddIntConstant(langs_module, "L_BATCH", L_BATCH);
	PyModule_AddIntConstant(langs_module, "L_INI", L_INI);
	PyModule_AddIntConstant(langs_module, "L_NFO", L_NFO);
	PyModule_AddIntConstant(langs_module, "L_USER", L_USER);
	PyModule_AddIntConstant(langs_module, "L_ASP", L_ASP);
	PyModule_AddIntConstant(langs_module, "L_SQL", L_SQL);
	PyModule_AddIntConstant(langs_module, "L_VB", L_VB);
	PyModule_AddIntConstant(langs_module, "L_JS", L_JS);
	PyModule_AddIntConstant(langs_module, "L_CSS", L_CSS);
	PyModule_AddIntConstant(langs_module, "L_PERL", L_PERL);
	PyModule_AddIntConstant(langs_module, "L_PYTHON", L_PYTHON);
	PyModule_AddIntConstant(langs_module, "L_LUA", L_LUA);
	PyModule_AddIntConstant(langs_module, "L_TEX", L_TEX);
	PyModule_AddIntConstant(langs_module, "L_FORTRAN", L_FORTRAN);
	PyModule_AddIntConstant(langs_module, "L_BASH", L_BASH);
	PyModule_AddIntConstant(langs_module, "L_FLASH", L_FLASH);
	PyModule_AddIntConstant(langs_module, "L_NSIS", L_NSIS);
	PyModule_AddIntConstant(langs_module, "L_TCL", L_TCL);
	PyModule_AddIntConstant(langs_module, "L_LISP", L_LISP);
	PyModule_AddIntConstant(langs_module, "L_SCHEME", L_SCHEME);
	PyModule_AddIntConstant(langs_module, "L_ASM", L_ASM);
	PyModule_AddIntConstant(langs_module, "L_DIFF", L_DIFF);
	PyModule_AddIntConstant(langs_module, "L_PROPS", L_PROPS);
	PyModule_AddIntConstant(langs_module, "L_PS", L_PS);
	PyModule_AddIntConstant(langs_module, "L_RUBY", L_RUBY);
	PyModule_AddIntConstant(langs_module, "L_SMALLTALK", L_SMALLTALK);
	PyModule_AddIntConstant(langs_module, "L_VHDL", L_VHDL);
	PyModule_AddIntConstant(langs_module, "L_KIX", L_KIX);
	PyModule_AddIntConstant(langs_module, "L_AU3", L_AU3);
	PyModule_AddIntConstant(langs_module, "L_CAML", L_CAML);
	PyModule_AddIntConstant(langs_module, "L_ADA", L_ADA);
	PyModule_AddIntConstant(langs_module, "L_VERILOG", L_VERILOG);
	PyModule_AddIntConstant(langs_module, "L_MATLAB", L_MATLAB);
	PyModule_AddIntConstant(langs_module, "L_HASKELL", L_HASKELL);
	PyModule_AddIntConstant(langs_module, "L_INNO", L_INNO);
	PyModule_AddIntConstant(langs_module, "L_SEARCHRESULT", L_SEARCHRESULT);
	PyModule_AddIntConstant(langs_module, "L_CMAKE", L_CMAKE);
}

PyMODINIT_FUNC PyInit_pynpp(void)
{
	PyObject* m;
	m = PyModule_Create(&pynpp_method);
	if (NULL == m)
	{
		ERR_WIN(L"PyInit_pynpp error.\n");
	}
	SetupConsole(m);
	SetupConst(m);
	SetupNpp(m);
	SetupEditor(m);

	return m;
}

void SetupPynpp()
{
	SendSci(SCI_CLEARALL, 0, 0);

	// Create the module
	//if ((pynpp_module = Py_InitModule3("pynpp", no_methods,
	//	"This is the pynpp module!!! Python plugins for Notepad++. Hurray.\n\n"
	//	"Author: Joao Moreno <alph.pt [on] gmail.com>\n"
	//	"Site: http://code.google.com/p/pynpp/ \n"
	//	"Full Docs:http://code.google.com/p/pynpp/wiki/pynppModuleReference \n"
	//	)) == NULL)
	if ((pynpp_module = PyInit_pynpp()) ==NULL)
	{
		ERR_WIN(L"Error initializing pynpp module.");
		_exit(-1);
	}
	
	ImportStdFile();
	// initPluginClass();

	// Import the module
	PyObject* pyRet;
	pyRet = PyRun_String("import pynpp", Py_single_input, globals, globals);
	if (PyErr_Occurred()) PyErr_Print();
	if (NULL == pyRet)
	{
		ERR_WIN(L"SetupPynpp error.");
	}
	Py_XDECREF(pyRet);

	// pynpp all set!

	// Little trick to get it going
	// http://www.python.org/doc/faq/windows/#pyrun-simplefile-crashes-on-windows-but-not-on-unix-why
	//#ifndef _DEBUG
	//	int ret = PyRun_SimpleString("execfile(\"startup.py\")\n");
	//	if (ret < 0)
	//	{
	//		INFO_WIN(L"PyRun_SimpleString Error.");
	//	}
	//	else
	//	{
	//		INFO_WIN(L"PyRun_SimpleString Successful.");
	//	}
	//#else
	//	FILE *fp;
	//	errno_t err;
	//	err = fopen_s(&fp, "pynpp.py", "r+");
	//	if ( err != 0 )
	//		PyRun_SimpleFile(fp, "pynpp.py");
	//	else
	//		ERR_WIN(L"No pynpp.py!");
	//	fclose(fp);
	//#endif

	PromptPy();
}

void DeletePynpp()
{
}

void LogPy(int length, const char* msg)
{
	SendSci (SCI_ADDTEXT, length, (LPARAM) msg);
	sciIN += length;
	SendSci (SCI_GOTOPOS, sciIN, 0);
}

void PromptPy() {PromptPy("", 0);}

void PromptPy(const char *indent, size_t size)
{
	if ( size == 0 )
	{
		SendSci (SCI_ADDTEXT, 4, (LPARAM) ">>> ");
		sciIN = (int) SendSci (SCI_GETCURRENTPOS, 0, 0);
		sciINEND = sciIN;
	}
	else
	{
		SendSci (SCI_ADDTEXT, 4, (LPARAM) "... ");
		sciIN = (int) SendSci (SCI_GETCURRENTPOS, 0, 0);
		SendSci (SCI_ADDTEXT, size, (LPARAM) indent);
		sciINEND = (int) SendSci (SCI_GETCURRENTPOS, 0, 0);
	}
}

#ifdef DEVELOPMENT_PHASE
// Plugin type

typedef struct {
    PyObject_HEAD
} pynpp_PluginObject;

static PyTypeObject pynpp_PluginType =
{
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "pynpp.Plugin",             /*tp_name*/
    sizeof(pynpp_PluginObject), /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    0,                         /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,        /*tp_flags*/
    "Plugin class for pynpp",           /* tp_doc */
};

void initPluginClass()
{
    pynpp_PluginType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&pynpp_PluginType) < 0)
        return;

    Py_INCREF(&pynpp_PluginType);
    PyModule_AddObject(pynpp_module, "Plugin", (PyObject *)&pynpp_PluginType);
}
#endif
